#include "../sleipner_config.h"

#include "window_handler_sdl.h"

#ifdef _PLATFORM_SDL
#include "input/mouse.h"
#include "input/keyboard.h"
#include "input/joystick.h"
#include "gameflow/game_flow_manager.h"
#include "gameflow/ui_popup.h"
#include "ui/ui_manager.h"
#include "graphics/graphics.h"
#include "graphics/graphics_util.h"
#include "audio/audio.h"
#include "resman/resman.h"
#include "font/font_manager.h"
#include "util/timer.h"		    // Needs to come in early
#include "util/mmgr.h"		    // Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"	


#ifdef __APPLE__
	#ifndef _PLATFORM_IOS
		#include <Carbon/Carbon.h>
	#else   // IOS 
		#include "ios_wrappers.h"
	#endif

	#include <CoreFoundation/CFString.h>
	#include <CoreFoundation/CFBase.h>
	#include <CoreFoundation/CFStringEncodingExt.h>
	#include "NSCursorWrappers.h"
#endif

#ifdef _PLATFORM_ANDROID
	#ifdef __cplusplus
		extern "C"
		{
	#endif
	#include "SDL2/include/SDL_system.h"
	#include "SDL2/src/core/android/SDL_android.h"
	#ifdef __cplusplus
		}
	#endif
#endif


WindowHandlerSDL::WindowHandlerSDL():
m_ShowCustomMouse(true),
m_CustomCursor(NULL)
{
}


WindowHandlerSDL::~WindowHandlerSDL()
{
#ifdef __APPLE__ 
#ifndef  _PLATFORM_IOS
	if(m_CustomCursor)
		ReleaseCocoaCursor((CursorRef*)m_CustomCursor);
#endif
#endif
}

bool		WindowHandlerSDL::InitWindow(const std::string& Name, const std::string& Title, int	X, int Y, int Width, int Height) 
{
	m_Title = Title;
	m_AppName = Name;
	m_Width = Width;
	m_Height = Height;
    
	m_x			 = (m_OrigWidth -Width)/2;	
    m_y			 = (m_OrigHeight -Height)/2;			
    m_BaseWidth = m_Width = Width;
    m_BaseHeight = m_Height = Height;	
	GraphicsUtil::SetWindowSize(Width, Height);
	return true;
}


//#ifdef _ENABLE_OPENGL_
#include "oglgraphics/ogl_graphics.h"
extern void		SLEIPNER_CREATE_OGL();
//#endif //_ENABLE_OPENGL_

bool		WindowHandlerSDL::InitGraphics()
{
    //#ifdef _ENABLE_OPENGL_
    
    SLEIPNER_CREATE_OGL();
	if(!GetOGLGraphics()->Init())
	{
		_LOG(MSG_FATAL, "Unable to initialize opengl graphics.");
		return false;		
	}
	
	m_HasFocus = true;
    
#ifdef _SDL2
    // TODO: find out equalvalent
#else
    
	SDL_EnableUNICODE(true);
    SDL_WM_SetCaption( m_Title.c_str() , m_AppName.c_str());
#endif
    
    
	return true;
    //#endif
	return false;
	
}

bool		WindowHandlerSDL::DestroyWindow() 
{
	m_IsPaused = true;
	m_IsDestroyed = true;
	return true;
}

bool		WindowHandlerSDL::Tick(float dt)
{
	SDL_Event event;
	while( SDL_PollEvent( &event ) ) 
	{
		switch (event.type) {
                
#if defined(_PLATFORM_IOS) || defined(_PLATFORM_ANDROID)
            case SDL_FINGERMOTION:
            {
                input::Mouse::BUTTONS b = GET_MOUSE().MapTouchToButton(event.tfinger.fingerId);
               
            }
            break;
#endif
			case SDL_MOUSEMOTION:
         
// hack Iphone ony send me in 320x640 dimention although real resolustion is diff
#if defined(_PLATFORM_SDL) && !defined(_SDL2)
                
                
                m_MouseX = (float)event.button.x / 320.0f ;
				m_MouseY = (float)event.button.y / 480.0f;
                
                 //iPhone5 hack
                if(  ( GetGraphics()->GetScreenWidth() >= 1135.0f &&  GetGraphics()->GetScreenWidth() <= 1137.0f   ) ||
                     ( GetGraphics()->GetScreenHeight() >= 1135.0f &&  GetGraphics()->GetScreenHeight() <= 1137.0f   )
                   
                   )
                {
                    m_MouseX = (float)event.button.x /  ( m_Width * 0.5f );
                    m_MouseY = (float)event.button.y / ( m_Height * 0.5f );
                }
                
                //ipad hack
                if ( ( GetGraphics()->GetScreenWidth() >= 1023.0f &&  GetGraphics()->GetScreenWidth() <= 1025.0f   ) ||
                     ( GetGraphics()->GetScreenHeight() >= 1023.0f &&  GetGraphics()->GetScreenHeight() <= 1025.0f ) ||
                     ( GetGraphics()->GetScreenWidth() >= 1023.0f * 2.0f &&  GetGraphics()->GetScreenWidth() <= 1025.0f * 2.0f   ) ||
                    ( GetGraphics()->GetScreenHeight() >= 1023.0f * 2.0f &&  GetGraphics()->GetScreenHeight() <= 1025.0f * 2.0f )
                    ) 
                {
                    m_MouseX = (float)event.button.x / 768.0f;
                    m_MouseY = (float)event.button.y / 1024.0f;
                }
                 
                
#else
                m_MouseX = (float)event.button.x / (float)m_Width ;
				m_MouseY = (float)event.button.y / (float)m_Height;
#endif
				
				UTIL_CLAMP(m_MouseX, 0, 1);
				UTIL_CLAMP(m_MouseY, 0, 1);	
                
				GET_MOUSE().OnMouseMove(math::Vec2(m_MouseX, m_MouseY));
#ifdef __APPLE__ 
#ifndef  _PLATFORM_IOS                
                
				if(m_CustomCursor && m_ShowCustomMouse)
					SetCocoaCursor((CursorRef*)m_CustomCursor);
#endif                
#endif
                
                break;
			

#if defined( _PLATFORM_IOS) || defined (_PLATFORM_ANDROID)
            case SDL_FINGERUP:
            {
                input::Mouse::BUTTONS b = GET_MOUSE().MapTouchToButton(event.tfinger.fingerId);
                GET_MOUSE().OnMouseRelease(b);
            }
                break;
                
#else
            case SDL_MOUSEBUTTONUP:

				if (HasFocus())				
				{
					switch(event.button.button)
					{
                        case SDL_BUTTON_LEFT: 
                            GET_MOUSE().OnMouseRelease(input::Mouse::BUTTON_LEFT);
                            break;
                        case SDL_BUTTON_RIGHT:
                            GET_MOUSE().OnMouseRelease(input::Mouse::BUTTON_RIGHT);
                            break;
					}
				}
				break;
#endif
                
			
#if defined( _PLATFORM_IOS) || defined (_PLATFORM_ANDROID)
            case SDL_FINGERDOWN:            
            {
                input::Mouse::BUTTONS b = GET_MOUSE().MapTouchToButton(event.tfinger.fingerId);
                GET_MOUSE().OnMousePress(b);
                
                if(b == input::Mouse::BUTTON_RIGHT)
                     _LOG(MSG_COMMENT,"right button");
            }
                
            break;
            
#else
            case SDL_MOUSEBUTTONDOWN:

				if (HasFocus())
				{
					switch(event.button.button)
					{
                        case SDL_BUTTON_LEFT: 
                            GET_MOUSE().OnMousePress(input::Mouse::BUTTON_LEFT);
                            break;
                        case SDL_BUTTON_RIGHT:
                            GET_MOUSE().OnMousePress(input::Mouse::BUTTON_RIGHT);
                            break;
                        case SDL_BUTTON_X1:
                            GET_MOUSE().OnMousePress(input::Mouse::BUTTON_LEFT);                            
                            break;
                            
                        case SDL_BUTTON_X2:
                            GET_MOUSE().OnMousePress(input::Mouse::BUTTON_RIGHT);                           
                            break;
                            
                        default:
                            // hack: multi touch
                            // todo: re-write multi-touch codebase properly
                            GET_MOUSE().OnMousePress(input::Mouse::BUTTON_CENTER);
                            break;
					}
				}
				break;
#endif
                
			case SDL_KEYDOWN:		
				if (HasFocus())				
				{
					/*
					if((event.key.keysym.unicode & 0xFF80 ) == 0 )						
					{
						if( event.key.keysym.sym == SDLK_BACKSPACE )
						{}
						else
						{
							char ch = event.key.keysym.unicode & 0x7F;
							input::Keyboard::Instance().OnCharReceived(ch)	;				// Unicode input change to Assii  
						}
					}
					*/

					input::Keyboard::Instance().OnCharPressed(event.key.keysym.sym  );				
				}
				break;
				
                
			case SDL_KEYUP:
				if (HasFocus())				
				{					
					input::Keyboard::Instance().OnCharReleased(event.key.keysym.sym  );
#ifdef _PLATFORM_ANDROID
					

					if(//event.key.keysym.sym == KEY_ESCAPE ||
					   event.key.keysym.sym == 1073742094
							)
					{
						_LOG(MSG_COMMENT," inject key_escape ");
						input::Keyboard::Instance().OnCharPressed(KEY_ESCAPE);
						input::Keyboard::Instance().OnCharReleased(KEY_ESCAPE );						
						return false;

					}
#endif
				}
				break;
                

                
            break;
                

			case SDL_QUIT:
#if defined( _PLATFORM_IOS) || defined (_PLATFORM_ANDROID)

				res::ResMan::Instance().ReleaseAll();
				DestroyWindow();
				return false;

#else
#ifdef _NOLOCALIZATION_
				if( gameflow::UIPopupManager::Instance().StartYesNo(QUIT_YESNO, UI_DELEGATE(WindowHandlerSDL, OnHardQuit), NULL) == false)
#else
				if( gameflow::UIPopupManager::Instance().StartYesNo("QUIT_YESNO", UI_DELEGATE(WindowHandlerSDL, OnHardQuit), NULL) == false)
#endif				
				{
					OnHardQuit(ui::UIWidget::WE_LMB_CLICK,NULL);
				}
				return false;
#endif
				break;
                
#ifdef _SDL2
                // TODO: find out equalvalent
            case SDL_APP_WILLENTERBACKGROUND:
                if ( m_HasFocus )
                {
                    _LOG(MSG_WARNING, "LOST FOCUS");
                    m_HasFocus = false;
                    OnLooseFocus();
                }
                
                break;
            case SDL_APP_DIDENTERFOREGROUND:
                //if(!m_HasFocus)
                {
                    _LOG(MSG_WARNING, "GAINED FOCUS");
                    OnGainFocus();
                    //s_pSelf->OnEvent(AE_GAIN_FOCUS, NULL, NULL);
                    gameflow::GameFlowManager::Instance().SetLastGainFocus(TIMER().Time());
                    m_HasFocus = true;
                    
                }
                break;
                

#else
                
	case SDL_ACTIVEEVENT:
    #ifdef __APPLE__
        #ifndef  _PLATFORM_IOS
				if(event.active.gain)
				{
					if(m_CustomCursor)
						SetCocoaCursor( (CursorRef*)m_CustomCursor );
					m_ShowCustomMouse = true;
				}
				else
				{
					ThemeCursor( kThemeArrowCursor );
					m_ShowCustomMouse = false;
                }
        #endif  // _PLATFORM_IOS              
    #endif  // __APPLE__
                
				
                if ( event.active.state & SDL_APPACTIVE  )
                {
                    if ( event.active.gain )
                    {
                        if(!m_HasFocus)
                        {
                            _LOG(MSG_WARNING, "GAINED FOCUS");
                            OnGainFocus();
                            //s_pSelf->OnEvent(AE_GAIN_FOCUS, NULL, NULL);	
                            gameflow::GameFlowManager::Instance().SetLastGainFocus(TIMER().Time());
                            m_HasFocus = true;							
                            
                        }
                    } 
                    else if ( m_HasFocus )
                    {
                        _LOG(MSG_WARNING, "LOST FOCUS");
                        m_HasFocus = false;
                        OnLooseFocus();
                    }			
                }
                
                break;
#endif // _SDL2
                
			case SDL_JOYAXISMOTION:
				
				
				input::Joystick::Instance().OnTilt(event.jaxis.axis ,
                                                   event.jaxis.value );
                break;
                
#ifdef _PLATFORM_IOS                 
            case SDL_WINDOWEVENT_SIZE_CHANGED:
                // CHIT:
                // this one acutally need to hack SDL lib
                // because SDL won't send if w and h is same
                // but we need to disable that condition to get flipping upside down the phone
                // SDL_windoweventsc --> SDL_SendWindowEvent
                int test = 0;
                break;
#endif
		}
	}
	return true;
	
}

void DELEGATE_CALL	WindowHandlerSDL::OnHardQuit(ui::UIWidget::WIDGET_EVENT Event, ui::UIWidget* pWidget)
{
	DestroyWindow();	
}


void		WindowHandlerSDL::ResetDevice()
{
	
}

bool		WindowHandlerSDL::UpdateOS()
{
	if (HasFocus())
	{
		// give a little back to the OS
		SDL_Delay(1);
	} else
	{
#if defined( _PLATFORM_IOS) || defined (_PLATFORM_ANDROID)
        
#else
		SDL_Delay(100);
#endif
	}
	return true;
}


//not applicable to all, but for most
bool		WindowHandlerSDL::Minimize()
{
	return true;
}

bool		WindowHandlerSDL::Maximize()
{
	return true;
}

void		WindowHandlerSDL::SwitchFullscreen(int Width, int Height) 
{
	ToggleFullscreen();
}

void		WindowHandlerSDL::SwitchWindowed(int Width, int Height)
{
	ToggleFullscreen();
}

void		WindowHandlerSDL::ToggleFullscreen()
{
#ifdef _SDL2
    // TODO: find out equalvalent
#else
    
	
	if(SDL_WM_ToggleFullScreen(SDL_GetVideoSurface()))
	{
		// we successed
	}
	else
	{
		// oh no! we need to reload textures
		GetOGLGraphics()->InitWindow(m_Width,m_Height,!m_IsFullscreen);
		
		if(!m_IsQuitting )
		{
			res::ResMan::Instance().ReleaseAll();
			res::ResMan::Instance().ReinitAll();
			FontManager::Instance().Recreate();
			GetGraphics()->Recreate();
		}
	}
	m_IsFullscreen = !m_IsFullscreen;
#endif
    
	
}


std::string	WindowHandlerSDL::GetSaveFolder()
{
#ifdef _PLATFORM_ANDROID
	std::string Path = SDL_AndroidGetInternalStoragePath();
	_LOG(MSG_COMMENT, Path );
#endif

#ifdef __APPLE__
#ifndef  _PLATFORM_IOS	
	// Change working directory to resource folder
	CFBundleRef mainBundle = CFBundleGetMainBundle();
	CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
	char path[PATH_MAX];
	if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
	{
		// error!
	}
	CFRelease(resourcesURL);
	chdir(path);
	
	
	//locate user data folder
	FSRef fsRef;
	path[2048];
	if(noErr == FSFindFolder(kUserDomain, kPreferencesFolderType,
							 TRUE, &fsRef) &&
	   noErr == FSRefMakePath(&fsRef, (UInt8*)path, sizeof path))
		
	{
		std::string temp = path;
		return temp + "/";
	}
#else   // IOS
    
    
    //return "./Documents/"; 
    char TempPath[256];
    std::string Temp = GetUserDataSaveFolder(TempPath);
    Temp += "/";
    return Temp;
    
#endif
#endif
	
	return "./";
	
}

bool		WindowHandlerSDL::OnGainFocus()
{
#ifdef _PLATFORM_ANDROID

	//res::ResMan::Instance().FlushTextures();
	//GetGraphics()->Recreate();
	//FontManager::Instance().Recreate();
	AUDIO().RestartAll();
	_LOG(MSG_COMMENT, "Recreated Texture for Android");
#endif

	gameflow::GameFlowManager&	GM = gameflow::GameFlowManager::Instance();
	gameflow::GameState* pState = GM.GetCurrentState();
	//AUDIO().RestartAll();
	if (pState) pState->GainFocus();
	return true;
}

bool		WindowHandlerSDL::OnLooseFocus()
{
	gameflow::GameFlowManager&	GM = gameflow::GameFlowManager::Instance();
	gameflow::GameState* pState = GM.GetCurrentState();
	//AUDIO().PauseAll();
	if (pState) pState->LoseFocus();
    
	// if this is a fullscreen/widescreen and we had to change the resolution to do so
	// restore it and be nice.
    /*	if (g_bWidescreen && m_IsFullscreen && !g_bNativeWidescreen && g_DesktopResChanged)
     {
     // ADRIAN: simulate a fullscreen switch
     ShowWindow(m_hwnd, SW_MINIMIZE);
     RestoreDisplayRes();
     }*/
	//if (IsWidescreen() && IsFullscreen() && !IsNativeWidescreen() && IsResolutionChanged())
	//{
	//	_LOG(MSG_COMMENT, "Resolution was changed. Restoring...");
	//	ShowWindow(m_hwnd, SW_MINIMIZE);
	//	RestoreResolution();
	//}

#ifdef _PLATFORM_ANDROID
	AUDIO().PauseAll();
	//res::ResMan::Instance().FlushTextures();
#endif

	return true;
}


// functions to change screen resolution around
bool		WindowHandlerSDL::GetCurrentResolution(unsigned int& Width, unsigned int& Height, unsigned int &Depth)
{
	return true;
}

bool		WindowHandlerSDL::SetResolution(unsigned int Width, unsigned int Height, unsigned int Depth)
{
	return true;
}

bool		WindowHandlerSDL::DetectWidescreenMode()
{
	return true;
}

bool		WindowHandlerSDL::RestoreResolution()
{
	return true;
}

#ifdef __APPLE__
#ifndef  _PLATFORM_IOS
// Creates a CGImageRef from a URL.  Caller is responsible for disposing of it.
CGImageRef CGImageCreateFromURL( CFURLRef urlToImage )
{
	CGImageSourceRef	imageSource = CGImageSourceCreateWithURL( urlToImage, NULL );
	CGImageRef			theImage = nil;
	
	require( imageSource != nil, CantCreateImageSource );
	
	theImage = CGImageSourceCreateImageAtIndex( imageSource, 0, NULL );
	require( theImage != nil, CantGetImage );
	
	CFRelease( imageSource );
	
CantCreateImageSource:
CantGetImage:
	return( theImage );
}


CursorRef SetCursorFromCFURL( CFURLRef urlToImage )
{
	CGImageRef cgImageRef = CGImageCreateFromURL( urlToImage );
	CursorRef newCursor = NULL;
	
	require( cgImageRef != NULL, CantCreateImage );
	newCursor = CreateCocoaCursor( cgImageRef, 1, 1 );  // chit: 1 , 1 is hotspot , should pass as parameter?
	if ( cgImageRef )
		CFRelease( cgImageRef );
	
CantCreateImage:		
	return( (CursorRef)newCursor );
}

#endif
#endif

bool		WindowHandlerSDL::SetCursor(const std::string& File)
{
#if __APPLE__		
#ifndef  _PLATFORM_IOS	  	
	
	if(m_CurrentCursorName == File)
		return true;
	
	m_CurrentCursorName = File;
	
	CFStringRef CursorStrRef =  
	CFStringCreateWithCString(kCFAllocatorDefault, File.c_str(), GetApplicationTextEncoding());											
    
	CFURLRef	CfURL		= CFBundleCopyResourcesDirectoryURL( CFBundleGetMainBundle() );
	CFURLRef	FullUrlRef	= CFURLCreateCopyAppendingPathComponent( NULL, CfURL, CursorStrRef, false );	
	
	if(m_CustomCursor)
		ReleaseCocoaCursor((CursorRef*)m_CustomCursor);
    
	m_CustomCursor	= (CursorRef*)SetCursorFromCFURL( FullUrlRef );
	
	CFRelease( CfURL );
	CFRelease( FullUrlRef );
	
	if(m_CustomCursor == NULL)
		return false;
    
	if(m_ShowCustomMouse)
		SetCocoaCursor( (CursorRef*)m_CustomCursor );
	//HideCocoaCursor();
	
#endif
#endif
	return true;
}

#endif //_PLATFORM_SDL
